# coding: utf8


import re
import json
import asyncio
import requests
from aiowebsocket.converses import AioWebSocket
import threading
import logging


logging.basicConfig(level=logging.INFO)

# N2O,Af+G5QWnr9zNzaSdUEgYBiD9ik+5ZboVWNa7eTi2ltOu3jlexVGVZoeuYSUo1M2RG/AZn7BOIVAX1jfqNpeCLwXZ/u6rxD64bJystWMhZSHi3wAsKsNsK+vVQw64xQOZLkR+DGP8dS0yM5odDuuHMg==
# N2O,b4kdgIHBFa61C7nno63pGvF57ydNFC2hmwIBuujIJU5IRLfREF6qd+HC2nHLbe1Kuqk2jbwaIbApCu4d9NHGseul/MYgZKhRcNzs/QG/vEQCNzh7RBdumaxLgjdtdW0tY9lVYqyO11dJugoWGjumkQ==


def log(p, t, d):
    logging.info('[%s][%s] - %s', p, t, d)


async def start_main(pid):
    url = "wss://www.deribit.com/ws/main"

    async with AioWebSocket(url) as aws:
        converse = aws.manipulator

        msg = "N2O,{}".format(pid)
        log('main', 'send', msg)
        await converse.send(msg)

        async def ping():
            log('main', 'send', 'PING')
            await converse.send('PING')

        def ping_wrapper():
            asyncio.new_event_loop().run_until_complete(ping())
            threading.Timer(10, ping_wrapper).start()

        threading.Timer(10, ping_wrapper).start()

        while True:
            data = await converse.receive()
            log('main', 'recv', data)


msg_id = 1


def get_msg_id():
    global msg_id
    t = msg_id
    msg_id += 1
    return t


async def start_v2(channels):
    url = "wss://www.deribit.com/ws/api/v2/"

    msg_get_footer = json.dumps({"id": get_msg_id(), "method": "public/get_footer", "params": {}})
    msg_get_time = json.dumps({"id": get_msg_id(), "method": "public/get_time", "params": {}})
    msg_auth = json.dumps({"id": get_msg_id(), "method": "public/auth", "params": {"grant_type": "cookie"}})
    msg_hello = json.dumps({"id": get_msg_id(), "method": "public/hello", "params": {"client_name": "Deribit Frontend", "client_version": "1.33.0", "extensions": ["subscriptions"]}})
    msg_subscribe = json.dumps({"id": get_msg_id(), "method": "public/subscribe", "params": {"channels": channels}})
    msg_trade_volumns = json.dumps({"id": get_msg_id(), "method": "public/get_trade_volumes", "params": {}})

    async with AioWebSocket(url) as aws:
        converse = aws.manipulator

        async def ping():
            msg_ping = json.dumps({"id": get_msg_id(), "method": "public/ping", "params": {}})
            log('v2', 'send', msg_ping)
            await converse.send(msg_ping)

        def ping_wrapper():
            asyncio.new_event_loop().run_until_complete(ping())
            threading.Timer(10, ping_wrapper).start()

        threading.Timer(10, ping_wrapper).start()

        for msg in (msg_auth, msg_hello, msg_get_time):
            log('v2', 'send', msg)
            await converse.send(msg)
            data = await converse.receive()
            log('v2', 'recv', data)

        async def get_trade_volumns():
            log('v2', 'send', msg_trade_volumns)
            await converse.send(msg_trade_volumns)

        def get_trade_volumns_wrapper():
            asyncio.new_event_loop().run_until_complete(get_trade_volumns())
            threading.Timer(15, get_trade_volumns_wrapper).start()

        threading.Timer(15, get_trade_volumns_wrapper).start()

        while True:
            data = await converse.receive()
            log('v2', 'recv', data)


def preload():
    transition_ptn = re.compile(r"var transition = (\{[\s\S]+?\})")
    pid_ptn = re.compile(r"pid:\s*('[\s\S]+?')")
    mmfeature_ptn = re.compile(r'mmfutures: ([\s\S]+?\n)')

    channels = ["deribit_price_index.btc_usd", "deribit_volatility_index.btc_usd", "deribit_price_index.eth_usd", "deribit_volatility_index.eth_usd", "announcements", "instrument.state.any.any"]
    try:
        r = requests.get("https://www.deribit.com/main#/options?tab=all")
        if r.status_code == 200:
            transition = transition_ptn.search(r.text)
            transition = transition.group(1) if transition else ''

            if transition != '':
                pid = pid_ptn.search(transition).group(1).strip("'")
            else:
                raise RuntimeError("transition is None!")

            features = mmfeature_ptn.search(r.text)

            if features:
                features = features.group(1).rstrip(',\n')
                try:
                    features = eval(features)
                    features = features['currency']['btc']
                    [channels.append("ticker.{}.100ms".format(item['symbol'])) for exp_date, item in features['exp_date'].items()]

                except Exception as e:
                    raise e
            else:
                raise RuntimeError("mmfeatures is None!")

            return pid, channels
        else:
            r.raise_for_status()
    except Exception as e:
        raise e


class V2Thread(threading.Thread):

    def __init__(self, channels):
        super(V2Thread, self).__init__()
        self.channels = channels

    def run(self) -> None:
        try:
            asyncio.new_event_loop().run_until_complete(start_v2(channels))
        except KeyboardInterrupt:
            pass
        print("v2 Quit!")


if __name__ == '__main__':
    pid, channels = preload()

    V2Thread(channels).start()
    
    try:
        asyncio.get_event_loop().run_until_complete(start_main(pid))
    except KeyboardInterrupt:
        pass

    print("Quit!")








